import { Condition } from 'src/app/models';

declare type Operator = '\\=\\=\\=' | '\\=\\=' | '\\&\\&' | '\\|\\|' | '\\>\\=' | '\\<\\=' | '\\>' | '\\<' | '\\!\\=\\=' | '\\!\\=';

/**
 * 支持的运算符
 */
export const compareOperators: Operator[] = ['\\=\\=\\=', '\\=\\=', '\\&\\&', '\\|\\|', '\\>\\=', '\\<\\=', '\\>', '\\<', '\\!\\=\\=', '\\!\\='];

/**
 * 运算符对应的方法
 */
export const compareFns = {
    '===': (x: any, y: any) => {
        if (y === 'false' || y === 'true') {
            if (!x) {
                return false;
            }
            return x.toString() === y;
        }
        y = formatEmptyString(y);
        return x === y;
    },
    '==': (x: any, y: any) => {
        if (y === 'false' || y === 'true') {
            if (!x) {
                return false;
            }
            return x.toString() == y;
        }
        y = formatEmptyString(y);
        if (Array.isArray(x)) {
            return x.indexOf(y) > -1;
        } else {
            // tslint:disable-next-line: triple-equals
            return x == y;
        }
    },
    // && 和 || 总是返回满足条件判断时的值，而不是布尔值
    '&&': (x: any, y: any) => {
        return Boolean(x && y);
    },
    '||': (x: any, y: any) => {
        return Boolean(x || y);
    },
    '>': (x: any, y: any) => {
        const xIsNotNum = isNaN(x);
        const yIsNotNum = isNaN(y);
        return !xIsNotNum && !yIsNotNum && Number(x) > Number(y);
    },
    '<': (x: any, y: any) => {
        const xIsNotNum = isNaN(x);
        const yIsNotNum = isNaN(y);
        return !xIsNotNum && !yIsNotNum && Number(x) < Number(y);
    },
    '>=': (x: any, y: any) => {
        const xIsNotNum = isNaN(x);
        const yIsNotNum = isNaN(y);
        return !xIsNotNum && !yIsNotNum && Number(x) >= Number(y);
    },
    '<=': (x: any, y: any) => {
        const xIsNotNum = isNaN(x);
        const yIsNotNum = isNaN(y);
        return !xIsNotNum && !yIsNotNum && Number(x) <= Number(y);
    },
    '!=': (x: any, y: any) => {
        if (y === 'false' || y === 'true') {
            return x.toString() != y;
        }
        y = formatEmptyString(y);
        // tslint:disable-next-line: triple-equals
        return x != y;
    },
    '!==': (x: any, y: any) => {
        if (y === 'false' || y === 'true') {
            return x.toString() !== y;
        }
        y = formatEmptyString(y);
        return x !== y;
    }
};

function formatEmptyString(value: string) {
    if (typeof value === 'string' && (value === '""' || value === '\'\'')) {
        return '';
    } else {
        return value;
    }
}

export function compareDataField(data: any, condition: Condition) {
    let result = false;
    if (condition && condition.sentence) {
        const str = getFinalString(data, condition.sentence);
        result = compareSentence(data, str);
    }
    return result;
}

function getSplitRegx() {
    const regxSr = compareOperators.join('|');
    return new RegExp(`${regxSr}`, 'g');
}

function getFinalString(data: any, str: string) {
    const replacedStr = replaceStr(data, str);
    if (replacedStr) {
        return getFinalString(data, replacedStr);
    } else {
        return str;
    }
}

function replaceStr(data: any, str: string) {
    let startIndex = str.indexOf('(');
    if (startIndex > -1) {
        let endIndex = -1;
        const arrStr: string[] = Array.prototype.slice.call(str);
        for (const [index, value] of arrStr.entries()) {
            if (index < startIndex) {
                continue;
            } else if (value === '(') {
                startIndex = index;
            } else if (value === ')') {
                endIndex = index;
                break;
            }
        }
        if (endIndex > -1) {
            const sentence = str.substring(startIndex + 1, endIndex);
            const result = compareSentence(data, sentence);
            const length = endIndex - startIndex + 1;
            arrStr.splice(startIndex, length, `${result}`);
            return arrStr.join('');
        }
    }
    return null;
}

function compareSentence(data: any, fullSentence: string) {
    const regx = /&&|\|\|/g;
    const operators = fullSentence.match(regx) as ('&&' | '||')[];
    const sentences = fullSentence.split(regx);
    let isPass = false;
    const results: boolean[] = [];
    for (const sen of sentences) {
        const result = compareSingleSentence(data, sen);
        results.push(result);
    }
    isPass = compareBooArray(results, operators);
    return isPass;
}

function compareSingleSentence(data: any, sentence: string) {
    let result = false;
    if (sentence.trim() === 'false') {
        return false;
    } else if (sentence.trim() === 'true') {
        return true;
    }
    const splitRegx = getSplitRegx();
    const operators = sentence.match(splitRegx);
    if (operators && operators.length) {
        const operator = operators[0];
        const values = sentence.split(operator);
        const bindField = values[0].trim();
        const targetValue = values[1].trim();
        // tslint:disable-next-line: triple-equals
        // const hasKey = Object.prototype.hasOwnProperty.call(data, bindField);
        if (compareFns[operator]) {
            result = compareFns[operator](getDataBindFieldValue(data, bindField), targetValue);
        }
    }
    return result;
}

export function getDataBindFieldValue(data, bindField: string) {
    const tmp = bindField.split('.');

    let key = tmp[0];
    let bracketBeginIndex = key.indexOf('[');
    let arraryIndex: number;
    if (bracketBeginIndex > -1) {
        let bracketEndIndex = key.indexOf(']');
        arraryIndex = parseInt(key.substr(bracketBeginIndex + 1, bracketEndIndex - bracketBeginIndex - 1));
        key = key.substr(0, bracketBeginIndex);
    }

    let value = data[key];

    if (value == null) {
        return value;
    }

    if (bracketBeginIndex > -1) {
        value = value[arraryIndex];
    }
    if (Array.isArray(value)) {
        return value;
    } else if (tmp && tmp.length > 1) {
        tmp.splice(0, 1);
        return getDataBindFieldValue(value, tmp.join('.'));
    } else {
        return value;
    }
}

function compareBooArray(booArray: boolean[], operators: ('&&' | '||')[]) {
    let isPass = false;
    for (const [index, value] of booArray.entries()) {
        if (index === 0) {
            isPass = value;
        } else {
            const operator = operators[index - 1];
            if (operator) {
                isPass = compareFns[operator](isPass, value);
            }
        }
    }
    return isPass;
}
